/* ----------------------------------------------------------------------------
* File: box_connection.cpp
*
* Desc: Definition of CBoxConnection class
*
* Created: 22/02/2011, by Alexey Korshunov
* ---------------------------------------------------------------------------- */
#include "box_connection.h"
#include "utils.h"


// Cleanup function closes all opened sockets
// param - pointer to a conn_environment structure
void close_connection(void *param)
{
	conn_environment *env = static_cast<conn_environment*>(param);
	if (env == NULL)
		return;

	env->logger->Print("conn thread: close_connection\n");

	// close sockets
	if (env->control_sock != -1)
	{
		shutdown(env->control_sock, 2);
		close(env->control_sock);
	}

	if (env->data_sock != -1)
	{
		shutdown(env->data_sock, 2);
		close(env->data_sock);
	}

	env->logger->Print("conn thread: thread finished\n");

	pthread_mutex_lock(&env->mutex_stop);
	pthread_cond_signal(&env->cv_stop);
	pthread_mutex_unlock(&env->mutex_stop);

	// delete connection from server map
	env->stop = true;
	CBoxServer *server = env->conn->GetServer();
	if (server != NULL)
		server->CloseConnection(env->conn->GetId());
}


void detach_user_box(void *param)
{
	conn_environment *env = static_cast<conn_environment*>(param);
	if (env == NULL)
		return;

	env->logger->Print("conn thread: detach_user_box\n");

	CBoxServer *server = env->conn->GetServer();
	if (server != NULL)
		server->DetachUserBox(env->box->GetId(), env->conn);
}


// Thread function for each connection to client
// param - pointer to a conn_environment structure
void* conn_thread_func(void *param)
{
	int rcv_bytes;
	int status;
	box_trans_header hdr;

	// Disable cancellation of current thread
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

	conn_environment *env = static_cast<conn_environment*>(param);
	if (env == NULL)
		pthread_exit(NULL);

	CBoxConnection *conn = env->conn;
	CBoxServer *server = env->server;

	pthread_cleanup_push(&close_connection, (void*)env);

	const sockaddr_in *remoteaddr = conn->GetRemoteAddress();
	env->logger->Print("conn thread: thread id = %lu\n", pthread_self());
	env->logger->Print("conn thread: Conn info:\n");
	env->logger->Print("		data_sock = [%d]\n", env->data_sock);
	env->logger->Print("		control_sock = [%d]\n", env->control_sock);
	env->logger->Print("		id = [%d]\n", conn->GetId());
	env->logger->Print("		remote address = [%s]\n", inet_ntoa(remoteaddr->sin_addr));

	pthread_cond_signal(&env->cv_start);

	// Enable cancellation of current thread
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);

	// Authentification process
	char username[128];
	env->logger->Print("conn thread: Authentification...");
	if (conn->AuthProcess(username) != BOX_SERVER_OK)
		pthread_exit(NULL);
	env->logger->Print("success\n");

	// Open box
	env->logger->Print("conn thread: Attach to user box...\n");
	env->box = server->AttachUserBox(username, conn);
	if (env->box == NULL)
		pthread_exit(NULL);
	env->logger->Print("conn thread: success\n");

	pthread_cleanup_push(&detach_user_box, (void*)env);


	// Main connection cycle
	while(1)
	{
		// Receive trans header
		memset(&hdr, 0, sizeof(hdr));
		rcv_bytes = UtilsTCPReceive(env->data_sock, (char*)&hdr, sizeof(hdr), BOX_RECV_TIMEOUT, &status, env->logger->GetFileDescriptor());
		if (rcv_bytes == -1 && status == UTILS_CONNECTION_CLOSED)
			pthread_exit(NULL);

		if (rcv_bytes > 0)
		{
			env->logger->Print("conn thread: Received message, cmd - %lu, data_len - %lu, param1 - %lu, param2 - %lu, \n",
								hdr.cmd, hdr.data_len, hdr.param1, hdr.param2);
	
			if (hdr.cmd == CMD_GET_USER_INFO)
			{
				env->logger->Print("conn thread: command CMD_GET_USER_INFO\n");
				conn->GetUserInfoCmd(&hdr);
			}
			else if (hdr.cmd == CMD_GET_BOX_INFO)
			{
				env->logger->Print("conn thread: command CMD_GET_BOX_INFO\n");
				conn->GetBoxInfoCmd(&hdr);
			}
			else if (hdr.cmd == CMD_READ_FILE_DATA)
			{
				env->logger->Print("conn thread: command CMD_READ_FILE_DATA\n");
				conn->ReadFileDataCmd(&hdr);
			}
			else if (hdr.cmd == CMD_NEW_FILE_DATA)
			{
				env->logger->Print("conn thread: command CMD_NEW_FILE_DATA\n");
				conn->NewFileDataCmd(&hdr);
			}
			else if (hdr.cmd == CMD_UPDATE_FILE_DATA)
			{
				env->logger->Print("conn thread: command CMD_UPDATE_FILE_DATA\n");
				conn->UpdateFileDataCmd(&hdr);
			}
			else if (hdr.cmd == CMD_DELETE_FILE_DATA)
			{
				env->logger->Print("conn thread: command CMD_DELETE_FILE_DATA\n");
				conn->DeleteFileDataCmd(&hdr);
			}
			else
			{
				env->logger->Print("conn thread: command UNKNOWN\n");
			}
		}
	}

	pthread_cleanup_pop(1);		// detach_user_box
	pthread_cleanup_pop(1);		// close_connection
	pthread_exit(NULL);
}

CBoxConnection::CBoxConnection(CBoxServer *parent):
m_id(0),
m_init(false),
m_logger(NULL)
{
	m_env.server = parent;
}

CBoxConnection::~CBoxConnection()
{
	// Stop connection
	if (!m_env.stop)
		StopHandling();

	// Delete logger
	delete m_logger;
}

// Init connection
int CBoxConnection::Init(const conn_init_info& info)
{
	char log_name[512];
	int ret;

	m_id = info.id;

	// save info
	m_env.conn = this;
	m_env.data_sock = info.data_sock;
	m_env.control_sock = info.control_sock;
	memcpy(&m_env.remoteaddr, &info.remote_addr, sizeof(struct sockaddr_in));	

	// Create logger
	sprintf(log_name, "./conn_%d.log", info.data_sock);
	m_logger = new CFileLogger();
	if ((ret = m_logger->Open(log_name)) != BOX_SERVER_OK)
		return ret;
	m_env.logger = m_logger;

	m_init = true;
	return BOX_SERVER_OK;
}

// Start handling of connection
int CBoxConnection::StartHandling()
{
	struct timespec ts;

	if (!m_init)
	{
		m_env.logger->Print("ERROR: Connection must be initialized before start\n");
		return BOX_SERVER_ERROR;
	}

	if (!m_env.stop)
	{
		m_env.logger->Print("WARNING: Connection already started of handling\n");
		return BOX_SERVER_ERROR;
	}

	// create thread for handling
	if (UtilsThreadCreate(&m_thread, 0, &conn_thread_func, (void*)&m_env, m_env.logger->GetFileDescriptor()) != UTILS_OK)
		return BOX_SERVER_ERROR;

	// wait thread signal
	pthread_mutex_lock(&m_env.mutex_start);
	while(1)
	{
		clock_gettime(CLOCK_REALTIME, &ts);
		ts.tv_sec += BOX_START_THREAD_TIMEOUT;
		if (pthread_cond_timedwait(&m_env.cv_start, &m_env.mutex_start, &ts) != ETIMEDOUT)
			break;

		if (UtilsThreadIsExist(&m_thread) != UTILS_OK)	// thread doesn't exist already
			break;
	}
	pthread_mutex_unlock(&m_env.mutex_start);

	if (UtilsThreadIsExist(&m_thread) != UTILS_OK)
	{
		m_env.logger->Print("ERROR: Worker thread is failed\n");
		return BOX_SERVER_ERROR;
	}

	m_env.stop = false;
	m_env.logger->Print("Start handling\n");
	return BOX_SERVER_OK;
}

// Stop connection
int CBoxConnection::StopHandling()
{
	struct timespec ts;

	if (!m_init)
	{
		m_env.logger->Print("ERROR: Connection must be initialized before stop\n");
		return BOX_SERVER_ERROR;
	}

	if (m_env.stop)
	{
		m_env.logger->Print("WARNING: Connection already stopped of handling\n");
		return BOX_SERVER_ERROR;
	}

	// cancel handler thread
	if (UtilsThreadCancel(&m_thread, m_env.logger->GetFileDescriptor()) != UTILS_OK)
		return BOX_SERVER_ERROR;
	
	// wait thread
	pthread_mutex_lock(&m_env.mutex_stop);
	while(1)
	{
		clock_gettime(CLOCK_REALTIME, &ts);
		ts.tv_sec += BOX_STOP_THREAD_TIMEOUT;
		if (pthread_cond_timedwait(&m_env.cv_stop, &m_env.mutex_stop, &ts) != ETIMEDOUT)
			break;

		if (UtilsThreadIsExist(&m_thread) != UTILS_OK)	// thread already doesn't exist
			break;
	}
	pthread_mutex_unlock(&m_env.mutex_stop);

	m_env.stop = true;
	m_env.logger->Print("Stop handling\n");
	return BOX_SERVER_OK;
}


/************************************************************************/
/*	Desc: Get user info
	Params:
		header - pointer to a box_trans_header structure
		logger - pointer to a ILogger object
	Return:
		one of BOX_SERVER_xxx											*/
/************************************************************************/
int CBoxConnection::GetUserInfoCmd(const box_trans_header* header)
{	
	size_t len;
	unsigned long data_len;
	int rcv_bytes;
	char msg[1024];
	int status;

	// Skip message body
	m_env.logger->Print("conn thread: Skip %d body bytes\n", header->data_len);
	data_len = header->data_len;
	while(data_len)
	{
		len = (data_len >= sizeof(msg)) ? sizeof(msg) : data_len;
		rcv_bytes = UtilsTCPReceive(m_env.data_sock, msg, len, BOX_RECV_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
		if (rcv_bytes == -1)
		{
			if (status == UTILS_TIMEOUT)
				m_env.logger->Print("UtilsTCPReceive: timeout\n");
			return BOX_SERVER_ERROR;
		}

		data_len -= rcv_bytes;
	}

	// Get user info
	m_env.logger->Print("conn thread: Get profile info...");
	user_profile* pinfo = (user_profile*)m_env.box->GetProfileInfo();
	if (pinfo == NULL)
	{
		m_env.logger->Print("\nconn thread: ERROR - Unable to get profile info\n");
		return BOX_SERVER_ERROR;
	}
	m_env.logger->Print("OK\n");


	user_info uinfo;
	memset(&uinfo, 0, sizeof(uinfo));
	strncpy(uinfo.user_name, pinfo->username, strlen(pinfo->username) + 1);
	uinfo.num_boxes = pinfo->boxes.size();


	// Allocate buffer
	m_env.logger->Print("conn thread: Allocate memory...");
	len = sizeof(box_trans_header) + sizeof(unsigned int) + (pinfo->boxes.size() * sizeof(unsigned int));
	unsigned char* buf = (unsigned char*)malloc(len);
	if (buf == NULL)
	{
		m_env.logger->Print("\nconn thread: ERROR - Failed to allocate %d bytes for send buffer\n", len);
		return BOX_SERVER_OUT_OF_MEMORY;
	}
	m_env.logger->Print("OK\n");


	// Build output packet
	m_env.logger->Print("conn thread: Build output packet\n");
	box_trans_header hdr;
	memset(&hdr, 0, sizeof(hdr));
	hdr.cmd = header->cmd;
	hdr.data_len = sizeof(unsigned int) + (pinfo->boxes.size() * sizeof(unsigned int));
	hdr.time1970 = time(0);
	memcpy(buf, &hdr, sizeof(hdr));

	unsigned int *data_id = (unsigned int*)(buf + sizeof(hdr));
	*data_id = pinfo->boxes.size();
	data_id++;
	std::map<unsigned int, user_box_info*>::iterator it = pinfo->boxes.begin();
	for (; it != pinfo->boxes.end(); ++it)
	{
		*data_id = it->second->id;
		data_id++;
	}

	// Send packet
	int bytes;
	do 
	{
		bytes = UtilsTCPSend(m_env.data_sock, buf, len, BOX_SEND_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
		if (bytes == -1)
		{
			m_env.logger->Print("conn thread: ERROR - Unable to send %d bytes\n", len);
			free(buf);
			return BOX_SERVER_ERROR;
		}
	} while (bytes != len);
	m_env.logger->Print("conn thread: Send CMD_GET_USER_INFO packet, %d bytes\n", bytes);


	free(buf);
	return BOX_SERVER_OK;
}


/************************************************************************/
/*	Desc: Get box info
	Params:
		header - pointer to a box_trans_header structure
		logger - pointer to a ILogger object
	Return:
		one of BOX_SERVER_xxx											*/
/************************************************************************/
int CBoxConnection::GetBoxInfoCmd(const box_trans_header* header)
{
	size_t len;
	int rcv_bytes;
	unsigned long data_len;
	char msg[1024];
	int status;

	// Skip input packet body
	m_env.logger->Print("conn thread: Skip %d body bytes\n", header->data_len);
	data_len = header->data_len;
	while(data_len)
	{
		len = (data_len >= sizeof(msg)) ? sizeof(msg) : data_len;
		rcv_bytes = UtilsTCPReceive(m_env.data_sock, msg, len, BOX_RECV_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
		if (rcv_bytes == -1)
		{
			if (status == UTILS_TIMEOUT)
				m_env.logger->Print("UtilsTCPReceive: timeout\n");
			return BOX_SERVER_ERROR;
		}

		data_len -= rcv_bytes;
	}
	
	// Get profile info
	m_env.logger->Print("conn thread: Get profile info...");
	user_profile* pinfo = (user_profile*)m_env.box->GetProfileInfo();
	if (pinfo == NULL)
	{
		m_env.logger->Print("\nconn thread: ERROR - Unable to get profile info\n");
		return BOX_SERVER_ERROR;
	}
	m_env.logger->Print("OK\n");


	// Find box info
	m_env.logger->Print("conn thread: Find box info, id = %lu...", header->param1);
	std::map<unsigned int, user_box_info*>::iterator it_box;
	it_box = pinfo->boxes.find(header->param1);
	if (it_box == pinfo->boxes.end())
	{
		m_env.logger->Print("\nconn thread: ERROR - Incorrect box id\n");
		HandleCmdError("Incorrect box id");
		return BOX_SERVER_ERROR;
	}
	user_box_info *ubinfo = it_box->second;
	m_env.logger->Print("OK\n");

	
	len = sizeof(box_trans_header) + sizeof(unsigned int) + (ubinfo->files.size() * sizeof(unsigned int));

	// Allocate buffer
	unsigned char* buf = (unsigned char*)malloc(len);
	if (buf == NULL)
	{
		m_env.logger->Print("conn thread: ERROR - Failed to allocate %d bytes for send buffer\n", len);
		return BOX_SERVER_OUT_OF_MEMORY;
	}

	// Build output packet
	box_trans_header hdr;
	memset(&hdr, 0, sizeof(hdr));
	hdr.cmd = header->cmd;
	hdr.data_len = sizeof(unsigned int) + (ubinfo->files.size() * sizeof(unsigned int));
	hdr.time1970 = time(0);
	memcpy(buf, &hdr, sizeof(hdr));

	unsigned int *data_id = (unsigned int*)(buf + sizeof(hdr));
	*data_id = ubinfo->files.size();
	data_id++;
	std::map<unsigned int, file_info*>::iterator it_file = ubinfo->files.begin();
	for (; it_file != ubinfo->files.end(); ++it_file)
	{
		*data_id = it_file->second->id;
		data_id++;
	}

	// Send packet
	int bytes;
	do 
	{
		bytes = UtilsTCPSend(m_env.data_sock, buf, len, BOX_SEND_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
		if (bytes == -1)
		{
			m_env.logger->Print("conn thread: ERROR - Unable to send %d bytes\n", len);
			free(buf);
			return BOX_SERVER_ERROR;
		}
	} while (bytes != len);
	m_env.logger->Print("conn thread: Sent CMD_GET_BOX_INFO packet, %d bytes\n", bytes);

	free(buf);
	return BOX_SERVER_OK;
}


/************************************************************************/
/*	Desc: Read file data
	Params:
		conn_info - pointer to a box_conn_info structure
		header - pointer to a box_trans_header structure
	Return:
		one of BOX_SERVER_xxx											*/
/************************************************************************/
int CBoxConnection::ReadFileDataCmd(const box_trans_header* header)
{
	size_t len;
	int rcv_bytes;
	unsigned long data_len;
	char msg[1024];
	int status;

	// Skip input packet body
	m_env.logger->Print("conn thread: Skip %d body bytes\n", header->data_len);
	data_len = header->data_len;
	while(data_len)
	{
		len = (data_len >= sizeof(msg)) ? sizeof(msg) : data_len;
		rcv_bytes = UtilsTCPReceive(m_env.data_sock, msg, len, BOX_RECV_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
		if (rcv_bytes == -1)
		{
			if (status = UTILS_TIMEOUT)
				m_env.logger->Print("UtilsTCPReceive: timeout\n");
			return BOX_SERVER_ERROR;
		}

		data_len -= rcv_bytes;
	}

	// Get file info from box
	unsigned int id = header->param1;
	const file_info* finfo = m_env.box->GetFileInfoById(id);
	if (finfo == NULL)
	{
		HandleCmdError("Incorrect file identifier");
		return BOX_SERVER_ERROR;
	}

	// Allocate buffer
	size_t size = BOX_DEFAULT_READ_CHUNK;
	unsigned char* buf = (unsigned char*)malloc(size + sizeof(box_trans_header));
	if (buf == NULL)
	{
		m_env.logger->Print("ERROR: Failed to allocate %d bytes for send buffer\n", size + sizeof(box_trans_header));
		return BOX_SERVER_OUT_OF_MEMORY;
	}

	try
	{
		CUserBox::CUserFile file(finfo->name, "r");
		size_t file_size = file.GetSize();

		box_trans_header hdr;
		memset(&hdr, 0, sizeof(hdr));
		hdr.cmd = CMD_START_FILE_DATA;
		hdr.data_len = file_size < BOX_DEFAULT_READ_CHUNK ? file_size : BOX_DEFAULT_READ_CHUNK;
		hdr.time1970 = time(0);

		// Read data from file
		memcpy(buf, &hdr, sizeof(hdr));
		size_t bytes = file.ReadData((unsigned char*)(buf + sizeof(hdr)), hdr.data_len);
		if (bytes < 0)
			throw std::exception();

		len = bytes + sizeof(hdr);

		// Send data to client
		int send;
		do 
		{
			send = UtilsTCPSend(m_env.data_sock, buf, len, BOX_SEND_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
			if (send == -1)
			{
				m_env.logger->Print("conn thread: ERROR - Unable to send %d bytes\n", len);
				throw std::exception();
			}
		} while (send != len);
		m_env.logger->Print("conn thread: Sent CMD_START_FILE_DATA packet, %d bytes\n", send);


		// Send CMD_FILE_DATA packets
		file_size -= bytes;
		while(file_size > BOX_DEFAULT_READ_CHUNK)
		{
			memset(&hdr, 0, sizeof(hdr));
			hdr.cmd = CMD_FILE_DATA;
			hdr.data_len = BOX_DEFAULT_READ_CHUNK;
			hdr.time1970 = time(0);

			// Read data from file
			memcpy(buf, &hdr, sizeof(hdr));
			bytes = file.ReadData(buf + sizeof(hdr), hdr.data_len);
			if (bytes < 0)
				throw std::exception();

			len = bytes + sizeof(hdr);

			// Size data to client
			do 
			{
				send = UtilsTCPSend(m_env.data_sock, buf, len, BOX_SEND_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
				if (send == -1)
				{
					m_env.logger->Print("conn thread: ERROR - Unable to send %d bytes\n", len);
					throw std::exception();
				}
			} while (send != len);
			m_env.logger->Print("conn thread: Sent CMD_FILE_DATA packet, %d bytes\n", send);

			file_size -= bytes;
		}

		// Send CMD_END_FILE_DATA packet
		memset(&hdr, 0, sizeof(hdr));
		hdr.cmd = CMD_END_FILE_DATA;
		hdr.data_len = file_size;
		hdr.time1970 = time(0);

		// Read data from file
		memcpy(buf, &hdr, sizeof(hdr));
		bytes = file.ReadData(buf + sizeof(hdr), hdr.data_len);
		if (bytes < 0)
			throw std::exception();

		len = bytes + sizeof(hdr);

		// Size data to client
		do 
		{
			send = UtilsTCPSend(m_env.data_sock, buf, len, BOX_SEND_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
			if (send == -1)
			{
				m_env.logger->Print("conn thread: ERROR - Unable to send %d bytes\n", len);
				throw std::exception();
			}
		} while (send != len);
		m_env.logger->Print("conn thread: Sent CMD_END_FILE_DATA packet, %d bytes\n");
	}
	catch(...)
	{
		HandleCmdError("Exception by file processing");
		m_env.logger->Print("conn thread: ERROR - Exception by operate with user file\n");
	}

	free(buf);
	return BOX_SERVER_OK;
}


// New file data
int CBoxConnection::NewFileDataCmd(const box_trans_header* header)
{
	//int bytes = 0;

	//CBoxClient* client = conn_info->client;

	//// create buffer
	//size_t size = BOX_MAX_RECV_SIZE;
	//unsigned char* buf = (unsigned char*)malloc(size);
	//if (buf == NULL)
	//{
	//	server->logger->Error("Failed to allocate %d bytes for receive buffer", size);
	//	return -1;
	//}
	//// TODO: smart pointer for buffer

	//try
	//{
	//	// create file
	//	CUserBox::CUserFile file(name, "w");

	//	// copying process
	//	do
	//	{	
	//		bytes = client->RecvData(buf, size, BOX_RECV_TIMEOUT);
	//		if (bytes <= 0)
	//			break;

	//		file->WriteData(buf, bytes);
	//	}while(!bytes);
	//}
	//catch(std::exception& ex)
	//{
	//}
	//catch(...)
	//{

	//}

	//// Create file info
	//file_info* finfo = new file_info;
	//size_t len = strlen(name);
	//finfo->name = (char*)malloc(len + 1);
	//if (finfo->name == NULL)
	//	return -1;
	//strncpy(finfo->name, name, len);

	return BOX_SERVER_OK;
}


// Update file data
int CBoxConnection::UpdateFileDataCmd(const box_trans_header* header)
{
	//// create temporary copy
	//const char* old_name;
	//const char* new_name;
	//if (rename(old_name, new_name) == -1)
	//{
	//	logger->Error("Rename is failed for file %s %s", old_name, strerror(errno));
	//	err = true;
	//}

	//if (!err)
	//{
	//	try
	//	{
	//		// create file with new name
	//		CUserBox::CUserFile file(new_name, "w");

	//		// copying process
	//		int bytes = 0;
	//		char data_buf[BOX_MAX_RECV_SIZE];
	//		do
	//		{	
	//			bytes = recv(conn_info->client_sock, data_buf, sizeof(data_buf), 0)
	//				if (bytes <= 0)
	//					break;

	//			file->WriteData(data_buf, bytes);
	//		}while(!bytes);

	//		// create new file info
	//		file_info* 

	//			// delete temporary file
	//	}
	//	catch(std::exception& ex)
	//	{

	//	}
	//	catch(...)
	//	{

	//	}
	//}




	//if (!err)
	//{

	//}

	//// delete temporary copy

	return BOX_SERVER_OK;
}


// Delete file data
int CBoxConnection::DeleteFileDataCmd(const box_trans_header* header)
{
	return BOX_SERVER_OK;
}



/************************************************************************/
/*	Desc: Handling error
Params:
conn_info - pointer to a box_conn_info structure
header - pointer to a box_trans_header structure
error_msg - error message
Return:
one of BOX_SERVER_xxx											*/
/************************************************************************/
int CBoxConnection::HandleCmdError(const char* error_msg)
{
	int status;

	if (error_msg == NULL)
		return BOX_SERVER_ERROR;

	size_t len = strlen(error_msg) + 1;

	box_trans_header hdr;
	hdr.cmd = CMD_ERROR;
	hdr.data_len = len;
	hdr.param1 = 0;
	hdr.param2 = 0;
	hdr.time1970 = time(0);

	unsigned char* buf = (unsigned char*)malloc(sizeof(hdr) + len);
	if (buf == NULL)
	{
		m_env.logger->Print("ERROR: Failed to allocate %d bytes for send buffer\n", sizeof(hdr) + len);
		return BOX_SERVER_OUT_OF_MEMORY;
	}

	memcpy(buf, (void*)&hdr, sizeof(hdr));
	memcpy(buf + sizeof(hdr), (void*)error_msg, len);

	// Size data to client
	size_t size = sizeof(hdr) + len;
	int send = UtilsTCPSend(m_env.data_sock, buf, size, BOX_SEND_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
	if (send == -1)
	{
		m_env.logger->Print("conn thread: ERROR - Unable to send %d bytes\n", size);
		return BOX_SERVER_ERROR;
	}
	m_env.logger->Print("WARNING: Sent error message to client\n");

	return BOX_SERVER_OK;
}

// Log entry
//void CBoxConnection::LogEntry(const char *msg,...)
//{
//	char buf[1024];
//	va_list argptr;
//
//	va_start(argptr, msg);
//	vsnprintf(buf, sizeof(buf), msg, argptr);
//	m_logger->Print(buf);
//	va_end(argptr);
//}

int CBoxConnection::AuthProcess(char *username)
{
	int bytes;
	box_trans_header header;
	char buf[512];
	size_t data_len, len;
	int status;

	bool auth = false;
	while(!auth)
	{
		// Receive message header
		bytes = UtilsTCPReceive(m_env.data_sock, (char*)&header, sizeof(header), BOX_AUTH_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
		if (bytes == -1)
		{
			if (status == UTILS_TIMEOUT)
				m_env.logger->Print("\nconn thread: UtilsTCPReceive timeout, %d bytes\n", sizeof(header));
			return BOX_SERVER_ERROR;
		}

		if (bytes == sizeof(header))
		{
			char login[128];
			char password[128];

			// param1 - username length
			// param2 - password length
			if (header.cmd == CMD_AUTH && (header.data_len == header.param1 + header.param2))
			{
				bytes = UtilsTCPReceive(m_env.data_sock, buf, header.data_len, BOX_RECV_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
				if (bytes == -1)
				{
					if (status == UTILS_TIMEOUT)
						m_env.logger->Print("\nconn thread: UtilsTCPReceive timeout, %d bytes\n", header.data_len);
					return BOX_SERVER_ERROR;
				}
				
				memcpy(login, buf, header.param1);
				memcpy(password, buf + header.param1, header.param2);
				login[header.param1] = '\0';
				password[header.param2] = '\0';

				// TODO: checking username/password
				strncpy(username, login, strlen(login) + 1);

				// send answer
				memset(&header, 0, sizeof(header));
				header.cmd = CMD_AUTH;
				header.param1 = 0;
				header.time1970 = time(NULL);

				bytes = UtilsTCPSend(m_env.data_sock, &header, sizeof(header), BOX_SEND_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
				if (bytes == -1)
				{
					m_env.logger->Print("\nconn thread: ERROR - Unable to send %d bytes\n", sizeof(header));
					return BOX_SERVER_ERROR;
				}

				auth = true;
			}
			else
			{
				m_env.logger->Print("\nERROR - Incorrect auth packet, it will be skipped\n");

				// skip packet body
				size_t data_len = header.data_len;
				while(data_len)
				{
					len = (data_len >= sizeof(buf)) ? sizeof(buf) : data_len;
					bytes = UtilsTCPReceive(m_env.data_sock, buf, len, BOX_RECV_TIMEOUT, &status, m_env.logger->GetFileDescriptor());
					if (bytes == -1)
					{
						if (status == UTILS_TIMEOUT)
							m_env.logger->Print("\nconn thread: UtilsTCPReceive timeout, %d bytes\n", len);
						return BOX_SERVER_ERROR;
					}

					data_len -= bytes;
				}
			}
		}
	}

	return BOX_SERVER_OK;
}